import * as THREE from 'three';

const {sin,cos,tan, sqrt, min, max, random,PI} = Math;
const sphrical = new THREE.Spherical();
const q1 = new THREE.Quaternion(),zAxes = new THREE.Vector3(0,0,1),v1 = new THREE.Vector3();
/**
 * 锥形空间，角度不超过180
 * @param dir  基准方向 默认为z
 * @param angle  0-1 
 * @returns 
 */
export function  randomRadian(dir: THREE.Vector3,angle: number): THREE.Vector3 {
    q1.setFromUnitVectors(zAxes,dir);
    const phi  = (random()*angle + 0.5)*PI;
    const theta = (random() -0.5 )*angle *PI;
    const r2 = sin(phi);
    const x = r2*sin(theta);
    const z = r2*cos(theta);
    const y = cos(phi);
    v1.set(x,y,z);
    // v1.applyQuaternion(q1);
    return v1.clone();
}
export function randomDirectionInCone(dir: THREE.Vector3, theta: number): THREE.Vector3 {
    // 随机采样一个单位向量，夹角不超过 theta
    const u = random();
    const v = random();
    const phi = 2 * PI * u;
    const cosTheta = cos(PI * theta);
    const z = cosTheta + (1 - cosTheta) * v;
    const sinT = sqrt(1 - z * z);
    const x = sinT * cos(phi);
    const y = sinT * sin(phi);
    // 构造局部坐标系
    const axis = new THREE.Vector3();
    if (Math.abs(dir.x) < 0.99) axis.set(1,0,0); else axis.set(0,1,0);
    const tangent = new THREE.Vector3().crossVectors(dir, axis).normalize();
    const bitangent = new THREE.Vector3().crossVectors(dir, tangent).normalize();
    // 旋转到目标方向
    const local = new THREE.Vector3();
    local.addScaledVector(tangent, x);
    local.addScaledVector(bitangent, y);
    local.addScaledVector(dir.clone().normalize(), z);
    return local.normalize();
}

// 生成粒子时
export function randomRange   ( vMin: number, vMax: number): number {
    return vMin + Math.random() * (vMax - vMin);
}



export  const getSPEMat = (opt: THREE.PointsMaterialParameters,round = false) => {
    const mat = new THREE.PointsMaterial(opt);
    const vertParams = `
            attribute float a_Size;
            attribute float a_Angle;
            varying float v_Angle;
            void main() {
            `;
    const vertSize = `
            gl_PointSize = a_Size * size;
            v_Angle = a_Angle; 
            `;
    const fragParams = `
            vec2 rotateUV(vec2 uv, float angle) {
                float s = sin(angle);
                float c = cos(angle);
                mat2 rotationMatrix = mat2(c, -s, s, c);
                return rotationMatrix * (uv -0.5) + 0.5; 
            }
            varying float v_Angle;
            void main() {
            `;
    const fragAngle = `
    #if defined( USE_MAP ) || defined( USE_ALPHAMAP )
        #if defined( USE_POINTS_UV )
            vec2 uv = vUv;
        #else
            vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;
        #endif
        float c = cos(v_Angle);
        float s = sin(v_Angle);
        uv = rotateUV(uv, v_Angle);
        vec2 v2k =   step( vec2(0.,0.),uv)  * step(uv , vec2(1.,1.));
        gl_FragColor.a *= v2k.x * v2k.y;
    #endif
    #ifdef USE_MAP
        diffuseColor *= texture2D( map, uv );
    #endif
    #ifdef USE_ALPHAMAP
        diffuseColor.a *= texture2D( alphaMap, uv ).g;
    #endif
    `;
    mat.onBeforeCompile = (shader) => {
        shader.vertexShader = shader.vertexShader.replace(
            'void main() {', vertParams
        );
        shader.vertexShader = shader.vertexShader.replace(
            'gl_PointSize = size;', vertSize
        );
        shader.fragmentShader = shader.fragmentShader.replace(
            'void main() {', fragParams
        );
        shader.fragmentShader = shader.fragmentShader.replace(
            '#include <map_particle_fragment>', fragAngle
        );
        if(round){

            shader.fragmentShader = shader.fragmentShader.replace(
                '#include <alphatest_fragment>',`
                vec2 dxy = gl_PointCoord - vec2(0.5);
                diffuseColor.a *= smoothstep(0.27,0.2, dot(dxy, dxy) );
                #include <alphatest_fragment>
                `
            )
        }
    };
    return mat;
}
